/**
* @author Ashish Gupta
* CVSSP, University of Surrey
* Copyright (c) 2010 Ashish Gupta
* All rights reserved.
*/
/**
 * 
 */
package projFn;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Random;

/**
 * @author Ashish
 *
 */
public class Pop {
	private ArrayList<ProjFn> pop = new ArrayList<ProjFn>();
	
	/**
	 * @return
	 */
	public double computeMaxFitness(){
		double maxFitness = pop.get(0).getFitness();
		for(int i=0;i<pop.size();i++){
			if(maxFitness	> pop.get(i).getFitness())
				maxFitness = pop.get(i).getFitness();
		}
		return maxFitness;
	}
	
	/**
	 * @param numProjFn
	 * @param maxHashCode
	 * @param numBins
	 */
	public void spawnPop(int numProjFn,int maxHashCode,int numBins){
		this.pop = new ArrayList<ProjFn>(numProjFn);
		for(int i=0;i<numProjFn;i++){
			this.pop.add(new ProjFn(maxHashCode,numBins));
		}
	}
	
	/**
	 * @param imageSet
	 * @param dataSet
	 * @param numBins
	 */
	public void evolvePop(ImageSet imageSet, DataSet dataSet, int numBins){
		// compute Fitness of all projection functions
			// compute the histogram of each image in the training image set for each projection function
		int numImages = imageSet.getImageName().size();
			for(int i = 0; i < pop.size(); i++){
				Double[] posHist = new Double[numBins];
				Double[] negHist = new Double[numBins];
				for(int j = 0; j < numImages; j++){
					dataSet.getDataSet().get(imageSet.getImageName().get(j)).computeHistogram(pop.get(i));
					if(imageSet.getClassLabel().get(j) == 1){
						for(int k = 0; k < numBins; k++){
							posHist[k] += dataSet.getDataSet().get(imageSet.getImageName().get(j)).getHistogram()[k];
						}
					}
					else{
						for(int k = 0; k < numBins; k++){
							negHist[k] += dataSet.getDataSet().get(imageSet.getImageName().get(j)).getHistogram()[k];
						}
					}
				}			
				// compute the bhattacharya coefficient
				this.pop.get(i).setFitness(computeFitnessScore(posHist,negHist));
			}
			
		// select the top half and cross mutate randomly
			// sort the fitnessScore
			HashMap<Double,Integer> fitnessIndex = new HashMap<Double,Integer>(pop.size());
			Double[] fitnesses = new Double[pop.size()];
			for(int i=0;i<pop.size();i++){
				fitnesses[i] = this.pop.get(i).getFitness();
				fitnessIndex.put(this.pop.get(i).getFitness(), i);
			}
			Arrays.sort(fitnesses);
			Integer[] rankProjFn = new Integer[pop.size()];
			for(int i=0;i<pop.size();i++){
				rankProjFn[i] = fitnessIndex.get(fitnesses[i]);
			}
			
			ArrayList<ProjFn> mPop = new ArrayList<ProjFn>(pop.size());
			int numSelected = (pop.size()+1)/2;
			for(int i=0;i<numSelected;i++){
				mPop.add(pop.get(rankProjFn[i]));
			}
			for(int i=0;i<pop.size()-numSelected;i++){
				mPop.add(crossoverMutation(rankProjFn, numSelected));
			}
			this.pop = mPop;
	}

	/**
	 * @param pop2
	 * @param rankProjFn
	 * @return
	 */
	private ProjFn crossoverMutation(Integer[] rankProjFn, int numSelected) {
		Random rnd = new Random();
		int fidx = rnd.nextInt(numSelected);
		int midx = rnd.nextInt(numSelected);
		int blockLength = (int) (pop.get(fidx).getProjFn().size()*0.2);
		int position = rnd.nextInt(pop.get(fidx).getProjFn().size()-blockLength);
		ProjFn projFn = pop.get(fidx);
		for(int i = 0; i < blockLength;i++){
			projFn.getProjFn().set(position+i, pop.get(midx).getProjFn().get(position+i));
		}
		return projFn;
	}

	/**
	 * @param posHist
	 * @param negHist
	 * @return
	 */
	private Double computeFitnessScore(Double[] posHist, Double[] negHist) {
		double fitness = 0.0;
		for(int i = 0; i < posHist.length; i++){
			fitness += Math.sqrt(posHist[i]*negHist[i]);
		}
		return fitness;
	}

	/**
	 * @param pop the pop to set
	 */
	public void setPop(ArrayList<ProjFn> pop) {
		this.pop = pop;
	}

	/**
	 * @return the pop
	 */
	public ArrayList<ProjFn> getPop() {
		return pop;
	}
}
